package com.share.alibaba.agetway.config;

import com.alibaba.fastjson.JSON;
import com.share.alibaba.agetway.model.AccessLog;
import com.share.alibaba.agetway.service.AccessService;
import com.share.common.utils.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;
import java.util.Optional;

/**
 * @calssName LogFilter
 * @Description 全局日志打印, 请求日志以及返回日志, 并在返回结果日志中添加请求时间
 * @Order表示这个bean的注入初始化的顺序 Ordered 是同一个接口的执行顺序
 */
@Slf4j
@Component
public class LogFilter implements GlobalFilter, Ordered {
    @Autowired
    private AccessService accessService;

    @Override
    public int getOrder() {
        // -1 is response write filter, must be called before that
        return -1;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //1.提取http和https的请求
        String scheme = request.getURI().getScheme();
        if (!"https,http".contains(scheme)) {
            return chain.filter(exchange);
        }

        //2.文件上传不读取body
        String upload = request.getHeaders().getFirst("upload");
        if ("true".equals(upload)) {
            return chain.filter(exchange);
        }

        //创建基本对象
        long start = System.currentTimeMillis();
        AccessLog accessLog = makeAccessLog(exchange);
        accessLog.setStartTime(start);

        //响应结果处理,这个处理器是异步的
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                DataBufferFactory bufferFactory = this.bufferFactory();
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    Publisher<? extends DataBuffer> re = fluxBody.map(dataBuffer -> {
                        byte[] content = new byte[dataBuffer.readableByteCount()];// 创建缓冲区
                        dataBuffer.read(content);// 数据读入数组
                        DataBufferUtils.release(dataBuffer);// 释放掉内存

                        // 计算请求时间
                        long end = System.currentTimeMillis();
                        String responseStr = new String(content, Charset.forName("UTF-8"));
                        accessLog.setResponse(responseStr);
                        accessLog.setCostTime(end - start);
                        accessLog.setEndTime(end);
                        log.info("请求执行结束,请求日志accessLog={}", accessLog.toJSONString());
                        ThreadUtil.execute(() -> this.saveAccessLog(accessLog));
                        return bufferFactory.wrap(content);
                    });
                    return super.writeWith(re);
                } else {
                    return super.writeWith(body);
                }
            }

            //异步保存方法
            private void saveAccessLog(AccessLog accessLog) {
                int status = accessService.saveAccessLog(accessLog);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    //参数构建对象
    private AccessLog makeAccessLog(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();

        // 获取请求信息
        AccessLog accessLog = new AccessLog();
        accessLog.setMethod(request.getMethodValue());//method
        accessLog.setUri(request.getURI().getPath());//uri

        //ip地址
        String ip = null;
        HttpHeaders headers = request.getHeaders();
        ip = headers.getFirst("X-Forwarded-For");
        ip = Optional.ofNullable(ip).orElse(headers.getFirst("X-Real-IP"));
        ip = Optional.ofNullable(ip).orElse(request.getRemoteAddress().getAddress().getHostAddress());
        accessLog.setIp(ip);//host

        //提取请求参数
        String param = null;
        HttpMethod method = request.getMethod();
        if (method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH) {
            param = JSON.toJSONString(request.getBody());
        } else {
            param = request.getQueryParams().toString();
        }
        accessLog.setRequest(param);

        return accessLog;
    }
}
